Durante varios años me resistí a la idea de full-stack TypeScript. Venía de arquitecturas con Go o Python en el backend y TypeScript solo en el frontend, y la idea de unificar me parecía una concesión por conveniencia. Pero en los últimos dos años he trabajado en varios proyectos donde el stack es íntegramente TypeScript, y tengo opiniones más matizadas que las de entonces. Toca hacer balance.
El objetivo de este post no es convencer a nadie de que adopte full-stack TypeScript, ni desaconsejarlo, sino ofrecer un repaso honesto de qué he visto funcionar, qué sigue siendo problema, y en qué tipo de proyecto la elección tiene sentido.
Lo bueno
La ventaja más inmediata es el modelo de datos compartido. Cuando el mismo tipo User o Product vive en frontend y backend, y no tienes que escribirlo dos veces ni sincronizarlo con un generador externo, el coste cognitivo baja y los errores por desalineación desaparecen. Es de esas cosas que no aprecias hasta haberlas vivido, porque la alternativa (OpenAPI, Protobuf, duplicación manual) funciona, pero añade un paso constante de mantenimiento.
Herramientas como tRPC han convertido esto en algo mucho más que el tipo compartido. Con tRPC, un procedimiento en el backend es automáticamente una función tipada en el frontend, con autocompletado completo, validación en serialización y deserialización, y sin capa de generación de código externa. Es el tipo de desarrollo que se siente ágil de verdad, y que no tiene análogo directo fuera del mundo TypeScript.
La segunda ventaja real es el ecosistema de frameworks que asume full-stack desde el principio. Next.js con App Router, Remix (ahora fusionado con React Router), SvelteKit, Nuxt: todos están diseñados para que escribir una función en el servidor y consumirla desde el cliente sea trivial. Esta integración se ha vuelto tan buena que la línea entre frontend y backend se ha difuminado, y en proyectos de tamaño pequeño a mediano eso simplifica mucho.
Tercero, la calidad del tipado ha mejorado mucho. TypeScript 5.x ha introducido mejoras significativas en inferencia, tipos condicionales y constantes. El tipo derivado de un esquema Zod, por ejemplo, es a menudo más expresivo que lo que escribirías a mano, y la validación en tiempo de ejecución y la verificación en tiempo de compilación usan la misma fuente de verdad.
Lo regular
Hay zonas donde el balance es más ambiguo y depende de qué problema estés resolviendo.
El rendimiento del backend en Node.js es razonable pero no brillante. Para servicios IO-bound, está más que bien. Para servicios CPU-bound o con requisitos fuertes de latencia predecible, sigue habiendo alternativas más apropiadas (Go, Rust, incluso C# moderno). La aparición de Bun y Deno ha puesto presión saludable, y Bun especialmente tiene características muy interesantes (rendimiento de startup muy alto, ecosistema más ligero), pero la mayoría de proyectos reales siguen en Node por compatibilidad con el ecosistema npm y por el soporte empresarial.
La gestión de dependencias sigue siendo molesta. npm y pnpm han estabilizado mucho, pero tener 1.200 paquetes en node_modules en un proyecto modesto sigue siendo lo normal. La mayoría son transitivos y no los has elegido. Cada ataque a la cadena de suministro en paquetes populares recuerda que esta es una superficie grande.
El tamaño del bundle frontend también merece vigilancia. Con las librerías recientes (React Server Components, runtime más ligero, mejor tree-shaking) ha mejorado, pero es muy fácil que un proyecto se hinche sin darse cuenta hasta que el tiempo de carga lo hace evidente. Este es un problema estructural del modelo full-stack TypeScript y requiere disciplina permanente.
Lo malo
Hay cosas que siguen siendo francamente problemáticas.
El ecosistema de testing está más fragmentado que el del resto del lenguaje. Vitest se ha estabilizado como alternativa seria a Jest y es claramente mejor para proyectos nuevos, pero migrar proyectos grandes sigue siendo un dolor. Las librerías de mocking son numerosas y ninguna se ha convertido en estándar claro. La integración con frameworks como Next.js para tests de integración con rutas reales sigue sin ser de primer orden.
La fricción entre frontend y backend en servicios realmente grandes vuelve a aparecer. Cuando el proyecto crece lo suficiente, lo que parecía una sola base de código se parte en dos o tres, y el beneficio de compartir tipos se reduce. A escala muy grande, la ventaja sobre un backend en otro lenguaje con tipos generados puede desaparecer casi por completo.
La experiencia con TypeScript en librerías de terceros sigue siendo desigual. Algunas librerías están excelentemente tipadas; otras publican tipos incompletos o incorrectos. Cuando te topas con una definición mala, arreglarlo implica o bien forkear, o bien escribir declaraciones de tipo locales, o bien aguantar con any. Ninguna opción es agradable.
Y la compilación sigue pesando. Un proyecto TypeScript grande compila más lento que el equivalente en Go o en Rust, a veces por mucho. Las mejoras en TypeScript 5.x (project references, incremental, go-to-definition sin compilar) han aliviado pero no eliminado el problema. Para quien viene de lenguajes compilados rápidos, es un ajuste.
Cuándo tiene sentido
Después de probar varias configuraciones, mi recomendación actual es:
Full-stack TypeScript es una elección excelente para proyectos de producto de tamaño pequeño a mediano donde el equipo es pequeño, la iteración es rápida, y la cercanía entre frontend y backend es un valor. SaaS B2B, dashboards internos, herramientas admin, productos en etapa temprana donde las reglas de negocio cambian mucho y beneficiarse de tipos compartidos con cero fricción es una ventaja real.
Es una elección menos clara para sistemas con componentes heterogéneos, donde el backend hace trabajo pesado (procesamiento de datos, cálculos complejos, streaming de tiempo real), o donde varios equipos independientes trabajan en el mismo sistema. En esos casos, la uniformidad del lenguaje deja de compensar sus limitaciones.
Y es una mala elección cuando el requisito real es rendimiento sostenido alto, bajo uso de memoria o despliegue en entornos muy restringidos. Ahí hay lenguajes diseñados para eso y forzar Node.js es un ejercicio de terquedad.
Lo que viene
En 2025 espero ver consolidación más que revolución. Bun va a seguir presionando a Node, lo cual es bueno. Los frameworks como Next.js van a seguir difuminando la línea entre cliente y servidor. La tipificación automática de esquemas de base de datos (a la Drizzle ORM) va a extenderse. Y el ecosistema de herramientas como tRPC va a tener alternativas maduras en otras plataformas (hay trabajo en OpenAPI + tRPC puente).
Lo que no espero es una inversión del ciclo. Full-stack TypeScript ha ganado un hueco legítimo, y probablemente va a mantenerlo durante años. No es la solución universal, pero para una franja real del mercado es una elección razonable que bien implementada da resultados muy buenos. Eso es lo que he aprendido tras dos años, y es lo que me gustaría haber sabido al empezar.